# ~*~ coding: utf-8 ~*~
import time
import importlib
import os
from flask import current_app
from sqlalchemy.dialects.mysql import TINYINT

from apps.common.dbbase import BaseModel
from apps.common.dbtypes import JsonListTextField, JsonDictTextField
from apps.common.utils import generate_uuid, timestamp_obj_to_str
from apps.extensions import db
from .function import Function
from ...utils import gen_log_relative_path


__all__ = ['PeriodicTask', 'PeriodicTaskRunHistory']


class PeriodicTask(BaseModel):
    __tablename__ = 'tasks_periodic_task'

    id = db.Column(db.String(36), default=generate_uuid, primary_key=True)
    name = db.Column(db.String(200), unique=True)
    function_id = db.Column(db.String(36), nullable=True, default='')
    interval_id = db.Column(db.String(36), nullable=True, default='')
    crontab_id = db.Column(db.String(36), nullable=True, default='')
    args = db.Column(JsonListTextField, nullable=True)
    kwargs = db.Column(JsonDictTextField, nullable=True)
    enabled = db.Column(db.Boolean, default=True)
    only_once = db.Column(db.Boolean, default=False)
    description = db.Column(db.TEXT, nullable=True)

    create_by = db.Column(db.VARCHAR(32), nullable=True)
    create_time = db.Column(db.INT, default=time.time)
    update_time = db.Column(db.INT, default=time.time, onupdate=time.time)

    def run(self):
        history = PeriodicTaskRunHistory()
        history.task_id = self.id

        time_start = time.time()
        try:
            history.date_start = time_start
            history.save()

            # ---------- start execute function ----------
            function = db.session.query(Function).filter(Function.id == self.function_id).first()
            package = importlib.import_module('.'.join(function.method.split('.')[0:-1]))
            method = function.method.split('.')[-1]
            result = getattr(package, method)(*self.args, **self.kwargs, history=history)
            # ---------- end execute function ------------

            if result['data'] == 'Terminated':
                history.is_finished = 0
            else:
                history.is_finished = 1
            history.result = result['data']
            history.is_success = result['is_success']
        except Exception as e:
            current_app.logger.error(e)
            history.result = e.__class__.__name__
            history.is_finished = 0
            history.is_success = 0

        time_end = time.time()
        history.date_finished = time_end
        history.timedelta = time_end - time_start
        history.update()

    @property
    def has_running(self):
        """该任务是否有在运行的"""
        running_tasks = db.session.query(PeriodicTaskRunHistory). \
            filter_by(task_id=self.id, is_finished=2, is_success=2). \
            all()

        is_running = []
        for task in running_tasks:
            is_running.append(task.pid_is_exist)
        return any(is_running)

    def __str__(self):
        return '<PeriodicTask: {}>'.format(self.name)


class PeriodicTaskRunHistory(BaseModel):
    __tablename__ = 'tasks_periodic_task_history'

    id = db.Column(db.String(36), default=generate_uuid, primary_key=True)
    task_id = db.Column(db.String(36), nullable=True)
    date_start = db.Column(db.INT, nullable=True)
    date_finished = db.Column(db.INT, nullable=True)
    timedelta = db.Column(db.INT, default=0, nullable=True)
    log_path = db.Column(db.String(256), nullable=True)
    # 进程号ID
    process_id = db.Column(db.INT, nullable=True)

    # 0：异常；1：完成：2：运行
    is_finished = db.Column(TINYINT, default=2)
    # 0：失败；1：成功：2：运行
    is_success = db.Column(TINYINT, default=2)
    result = db.Column(db.TEXT, nullable=True)

    @property
    def pid_is_exist(self):
        """ Check For the existence of a unix pid. """
        try:
            os.kill(self.process_id, 0)
        except OSError:
            return False
        except TypeError:
            return False
        else:
            return True

    def kill(self):
        if self.pid_is_exist:
            os.kill(self.process_id, 15)
        self.is_success = 0
        self.is_finished = 0
        self.result = 'Terminated'
        self.update()

    def __str__(self):
        return '<TaskRunHistory: {}>'.format(self.id)
